package dbrouter;

import java.lang.reflect.*;
import java.rmi.server.UnicastRemoteObject;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import java.rmi.*;

public class BaseModelo extends UnicastRemoteObject implements IBaseModelo
{

    public BaseModelo() throws RemoteException
    {}

    public String teste(Object entidade)
    {
        return "teste concluido num 02";
    }

    @Override
    public <T> void inserir(T entidade)
    {
        //String nomeTabela = this.getClass().getSimpleName();
        String nomeTabela = entidade.getClass().getSimpleName();
        String nomeCampo, nomeMetodo;
        String metodoIdentificador = "";
        
        StringBuilder sqlValores = new StringBuilder();
        StringBuilder sqlCampos = new StringBuilder();
        
        String sqlInsert = "insert into " + nomeTabela + " (@campos) values (@valores)";
        Method metodo;
        
        for (Field item: entidade.getClass().getDeclaredFields())
        {
            nomeCampo = item.getName();            
            
            if (item.isAnnotationPresent(AutoNumerico.class))
            {
                metodoIdentificador = "set" + nomeCampo.substring(0,1).toUpperCase() + nomeCampo.substring(1);
                continue;
            }
            
            //Concatenar virgula para separação de campos
            if (sqlCampos.length() > 0)
            {
                sqlCampos.append(", ");
                sqlValores.append(", ");
            }
            
            sqlCampos.append(nomeCampo);
            
            nomeMetodo = "get" + nomeCampo.substring(0,1).toUpperCase() + nomeCampo.substring(1);
            
            try 
            {
                Object valorObj = Util.executarGetter(entidade, nomeMetodo);
                
                if (valorObj instanceof String)
                    sqlValores.append("'").append(valorObj).append("'");
                else
                    sqlValores.append(valorObj);
                
            } 
            catch (Exception ex) 
            {
                Logger.getLogger(BaseModelo.class.getName()).log(Level.SEVERE, null, ex);
            }           
                
        }
        
        sqlInsert = sqlInsert.replace("@campos", sqlCampos.toString());
        sqlInsert = sqlInsert.replace("@valores", sqlValores.toString());
        
        System.out.println(sqlInsert); 
        Persistencia persist = new Persistencia();
        int ultimoId = persist.executar(sqlInsert, true);
        persist.finalize();

        //Setar valor para o identificador
        try
        {
            Class[] tiposSet = new Class[] { int.class };
            metodo = entidade.getClass().getMethod(metodoIdentificador, tiposSet);
            metodo.invoke(entidade, new Object[]{ ultimoId });
        } 
        catch (Exception ex) 
        {
            Logger.getLogger(BaseModelo.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }
    
    @Override
    public <T> void atualizar(T entidade)
    {
        String nomeCampo, nomeMetodo, sqlClausula = "";
        StringBuilder sqlUpdate = new StringBuilder();
        StringBuilder sqlCampos = new StringBuilder();

        String nomeTabela = entidade.getClass().getSimpleName();
        
        //Instrução base para atualizar
        sqlUpdate.append("update ").append(nomeTabela).append(" set ");

        for (Field item: entidade.getClass().getDeclaredFields())
        {
            //Concatenar virgula para separação de campos  
            if(sqlCampos.length() > 0)
                sqlCampos.append(", ");

            nomeCampo = item.getName();
            nomeMetodo = "get" + nomeCampo.substring(0,1).toUpperCase() + nomeCampo.substring(1);
            String valor = null;

            try
            {
                Object valorObj = Util.executarGetter(entidade, nomeMetodo);

                if (valorObj instanceof String)
                    valor = "'" + valorObj + "'";
                else
                    valor = valorObj.toString();
                
            } 
            catch (Exception ex) 
            {
                Logger.getLogger(BaseModelo.class.getName()).log(Level.SEVERE, null, ex);
            }
                        
            if (item.isAnnotationPresent(AutoNumerico.class))
                sqlClausula = " where " + nomeCampo + " = " + valor;
            else
                sqlCampos.append(nomeCampo).append(" = ").append(valor);
        }

        sqlUpdate.append(sqlCampos).append(sqlClausula);
        System.out.println(sqlUpdate);

        Persistencia persist = new Persistencia();
        persist.executar(sqlUpdate.toString());
        persist.finalize();
    }
    
    @Override
    public <T> void remover(T entidade, int identificadorEntidade)
    {
        String nomeTabela = entidade.getClass().getSimpleName();
        String nomeCampo, nomeMetodo, sqlDelete;
        
        sqlDelete = "delete from " + nomeTabela + " where " +
                Util.getCampoIdentificador(entidade) + " = " + 
                String.valueOf(identificadorEntidade);
        
        System.out.println(sqlDelete);
        
        Persistencia persist = new Persistencia();
        persist.executar(sqlDelete.toString());
        persist.finalize();
    }
    
    @Override
    public <T> T obterPorId(T entidade, int identificadorEntidade)
    {
        T entidadeRetorno = null;
        Persistencia persist = new Persistencia();

        try
        {
            String nomeTabela = entidade.getClass().getSimpleName();

            StringBuilder sqlSelect = new StringBuilder();
            sqlSelect.append(" select * from ").append(nomeTabela);
            sqlSelect.append(" where ").append(Util.getCampoIdentificador(entidade));
            sqlSelect.append(" = ").append(identificadorEntidade);

            ResultSet rs = persist.executarConsulta(sqlSelect.toString());

            entidadeRetorno = mapearResultSet(entidade, rs);
            rs.close();
        }
        catch (SQLException ex)
        {
            Logger.getLogger(BaseModelo.class.getName()).log(Level.SEVERE, null, ex);
        }
        finally
        {
            persist.finalize();
        }
        
        return entidadeRetorno;
    }

    @Override
    public <T> ArrayList<T> obterTodos(T entidade)
    {
        ArrayList<T> listaEntidades = new ArrayList<T>();
        Persistencia persist = new Persistencia();
        ResultSet rs;
        T ent;

        try
        {
            String nomeTabela = entidade.getClass().getSimpleName();
            String sqlSelect = "select * from " + nomeTabela;
            
            rs = persist.executarConsulta(sqlSelect);
            
            do
            {
                entidade = (T) entidade.getClass().newInstance();
                ent = mapearResultSet(entidade, rs);
                listaEntidades.add(ent);
            } while (ent != null);
            
            //O ultimo elemento é sempre nulo
            listaEntidades.remove(listaEntidades.size() - 1);
            rs.close();
        }
        catch (Exception ex)
        {
            Logger.getLogger(BaseModelo.class.getName()).log(Level.SEVERE, null, ex);
        }
        finally
        {
            persist.finalize();
        }

        return listaEntidades;
    }

    private <T> T mapearResultSet(T entidade, ResultSet rs) throws SQLException
    {
        String nomeCampo, nomeMetodo;
        Method metodo;

        if (rs.next())
        {
            for (Field item: entidade.getClass().getDeclaredFields())
            {
                nomeCampo = item.getName();
                nomeMetodo = "set" + nomeCampo.substring(0,1).toUpperCase() + nomeCampo.substring(1);

                //Setar valor para o identificador
                try
                {
                    Object valor = rs.getObject(nomeCampo);
                    Class[] tiposSet = new Class[] { item.getType() };
                    metodo = entidade.getClass().getMethod(nomeMetodo, tiposSet);
                    metodo.invoke(entidade, new Object[]{ valor });
                }
                catch (Exception ex)
                {
                    Logger.getLogger(BaseModelo.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            return entidade;
        }
        else
        {
            return null;
        }
    }
    
}
